এজেন্টের ফলের JSON গঠন সঠিক হয়েও উৎসের ব্যাখ্যা ভুল হতে পারে। এই নির্দেশিকা Python ডেভেলপারকে একটি অনুমোদিত নোটের ফাইল থেকে স্থানীয় তালিকা পর্যন্ত পূর্ণ পথ দেয়: নির্দিষ্ট টাইপের উৎপাদকের ফল, উৎস ও অর্থ যাচাই, সঠিক হ্যাশে মানুষের অনুমোদন এবং দলবদ্ধ Markdown প্রতিবেদন লিখে পুনরায় পড়া ভোক্তা। নিজের কাজের স্পষ্ট প্রতিশ্রুতিসহ নোট ব্যবহার করুন। প্রস্তুতিতে বাস্তব নোট বা মডেলের ফল ব্যবহার হয়নি।
বড় পরিবর্তন
- যা বদলেছে: OpenAI Agents SDK একটি Pydantic
output_typeগ্রহণ করে, তাই অ্যাপ্লিকেশন নির্ধারিত ফল চেয়ে ব্যবহারের আগে আবার যাচাই করতে পারে। উদাহরণ ফলের সঙ্গে উৎসের উদ্ধৃতি ও সংস্করণযুক্ত চুক্তি যোগ করে। - কেন গুরুত্বপূর্ণ: এজেন্টকে পরের কাজের সঙ্গে যুক্ত করা ডেভেলপার ভুল গঠন, অসম্পূর্ণ বা ভিত্তিহীন আউটপুট করণীয় তালিকা হওয়ার আগে আটকাতে পারেন। উদ্ধৃত নোট সত্যিই প্রতিটি কাজ, দায়িত্বশীল ও তারিখ সমর্থন করে কি না এবং মডেল কিছু বাদ দিয়েছে কি না মানুষকেই বিচার করতে হবে।
- যা খেয়াল রাখবেন: সিদ্ধান্তের জায়গা ভোক্তার সীমানা। এই পদ্ধতি সঠিক রান, উৎস ও প্রার্থীর অনুমোদনের পরেই স্থানীয় বাছাই তালিকা তৈরি করে। পরে কাজের ব্যবস্থায় আমদানির জন্য আলাদা কর্তৃত্ব, পুনঃচেষ্টার নীতি ও গন্তব্যে পুনরায় পড়ে যাচাই লাগবে।
একটি অনুমোদিত উৎস প্রস্তুত করুন
Python 3.12, নোটের জন্য অনুমোদিত OpenAI API অ্যাকাউন্ট, মূল ফাইল পড়তে পারা পর্যালোচক এবং অ্যাকাউন্টে বর্তমানে JSON-schema কাঠামোবদ্ধ আউটপুট সমর্থন করে বলে নিশ্চিত মডেল লাগবে। উৎপাদক চালানোর আগে তথ্যের শর্ত ও বর্তমান মূল্য দেখুন। নোট বা অনুমোদন ফাইলে পরিচয়-তথ্য দেবেন না। নিচে একমাত্র মডেল কল produce; বাকি কাজ স্থানীয়।
মূল নোট UTF-8 হিসেবে notes.txt এ রাখুন। নিজের কাজের বাস্তব স্পষ্ট করণীয় প্রতিশ্রুতি থাকতে হবে। নিজস্ব রেকর্ড পরিচয়ের মতো স্থির source_id দিন এবং পর্যালোচনা ও ব্যবহার পর্যন্ত ফাইলের একটি বাইটও বদলাবেন না। তৈরির আগে candidate/ তৈরি করুন; প্রার্থীর ফাইল নতুন হতে হবে। পরের স্থানীয় প্রতিবেদনের জন্য আলাদা আউটপুট ডিরেক্টরি রাখুন।
চালানোর আগে উৎসের বাইটের হ্যাশ লিখুন:
python -c "from pathlib import Path; import hashlib; print(hashlib.sha256(Path('notes.txt').read_bytes()).hexdigest())"আলাদা Python 3.12 পরিবেশে recipe-requirements.txt তৈরি করে ঠিক এই লাইন দিন এবং ইনস্টল করুন:
openai-agents==0.24.0
pydantic==2.14.1python -m pip install -r recipe-requirements.txt
mkdir candidateএই সংস্করণগুলো ২০ অক্টোবর ২০২৬-এ স্থানীয় Python 3.12.7 পরিবেশে একসঙ্গে ইনস্টল হয়েছিল। ব্যবহারকালে সামঞ্জস্য ও মডেলের প্রাপ্যতা যাচাই করুন। নিচের পূর্ণ স্ক্রিপ্ট action_register_recipe.py নামে একই ডিরেক্টরিতে রাখুন। এর দুটি কমান্ড: produce প্রার্থী ও রানের রসিদ বানায়; consume যাচাই ও অনুমোদনের পরেই প্রতিবেদন বানায়।
নির্দিষ্ট টাইপের উৎপাদক চালান
চুক্তি Extraction তে সংস্করণ action-register/v1, অবস্থা ready, partial বা blocked, উৎসের ID ও ফাইলের সঠিক বাইটের SHA-256, করণীয় এবং gap_note লাগে। প্রতি করণীয়তে লেখা, সঠিক উদ্ধৃতি ও ১ থেকে গোনা উৎসের লাইনসীমা, null হতে পারা দায়িত্বশীল ও তারিখ এবং complete বা needs_review প্রমাণের অবস্থা লাগে। দায়িত্বশীল বা তারিখ null মানে নোটে বলা নেই। মডেলকে কোনোটিই অনুমান না করতে বলা হয়। সব ঘর আবশ্যক এবং অতিরিক্ত ঘর প্রত্যাখ্যাত হয়।
কাঠামোবদ্ধ আউটপুটের মডেল স্পষ্ট করে বাছুন। নিজের অনুমোদিত পথ ও পরিচয় দিন:
python action_register_recipe.py produce --source-id OWNED-ID --source notes.txt --candidate candidate/actions.json --model YOUR-STRUCTURED-OUTPUT-MODELপড়ুন candidate/actions.json এবং পাশে candidate/actions.run.json। দ্বিতীয়টি UUID রান ID, উৎস ও প্রার্থীর হ্যাশ, শেষ এজেন্ট ও অনুরোধের মডেল লিখে। ব্যর্থতায় বদলে রান ID-যুক্ত .failure.json রোগনির্ণয় রেকর্ড হয়। বাধা, None বা সাধারণ স্ট্রিং চূড়ান্ত আউটপুট, কিংবা অপ্রত্যাশিত শেষ এজেন্ট সফলতা নয়। কমান্ড SDK-এর আবার চালু করা যায় এমন RunState রাখে না; ব্যর্থতার রসিদ রান পুনরায় চালু করতে পারে না। নতুন রান উপযুক্ত কি না ঠিক করার আগে অনুরোধ, প্রার্থী ও পরের অবস্থা দেখুন।
অর্থ পর্যালোচনা করে সঠিক প্রার্থী অনুমোদন করুন
অ্যাপ্লিকেশন কঠোর টাইপ ও আবশ্যক ঘর, উৎসের পরিচয়, প্রস্তুত অবস্থা, অন্তত একটি পূর্ণ করণীয়, বৈধ লাইনসীমা, তার মধ্যে উদ্ধৃতি, ফাঁকা নয় এমন কাজ বা দায়িত্বশীল এবং মানসম্মত YYYY-MM-DD তারিখ যাচাই করে। এটি গঠন ও উদ্ধৃতির অবস্থান প্রমাণ করে। বাক্য সত্যিকারের প্রতিশ্রুতি, নাম বা তারিখের অর্থ প্রার্থীর দাবি অনুযায়ী, বা সব প্রতিশ্রুতি পাওয়া গেছে—তা প্রমাণ করে না।
মূল নোট ও প্রতি প্রস্তাবিত করণীয় পড়ুন। অর্থ, দায়িত্বশীল, তারিখ, উদ্ধৃতি ও বাদ পড়া বিষয় তুলুন। উৎস বা প্রার্থীর অস্পষ্টতা নতুন পর্যালোচিত রানে মেটান; ভিত্তিহীন, আংশিক বা অবরুদ্ধ প্রার্থী অনুমোদন করবেন না। অপরিবর্তিত ফাইল ও প্রার্থীর সঙ্গে actions.run.json মেলে দেখে approval.json ঠিক এই সাতটি কী দিয়ে লিখুন। যাচাই করা রসিদ থেকে রান ID ও হ্যাশ নিন। পর্যালোচকের সত্যিকারের পরিচয় ও বাস্তবে কী যাচাই হয়েছে তার যথেষ্ট বিবরণ দিন:
{
"approved": true,
"run_id": "COPY-RUN-ID",
"source_id": "OWNED-ID",
"source_sha256": "COPY-SOURCE-SHA256",
"payload_sha256": "COPY-PAYLOAD-SHA256",
"reviewer": "YOUR-REVIEWER-ID",
"review_note": "Describe the source, task, owner, date, quotation and omission checks actually performed."
}approved JSON বুলিয়ান true হতে হবে, স্ট্রিং বা সংখ্যা নয়। অন্য সব মান স্ট্রিং; পর্যালোচক ও মন্তব্য ফাঁকা হতে পারবে না। অতিরিক্ত ঘর ব্যর্থ হয়। অনুমোদন একটি প্রার্থী ও উৎসের সংশোধনকে বাঁধে, ভবিষ্যৎ পুনর্লিখনকে নয়।
ব্যবহার করে প্রতিবেদন পুনরায় পড়ুন
python action_register_recipe.py consume --source-id OWNED-ID --source notes.txt --candidate candidate/actions.json --approval approval.json --out private-outputভোক্তা গৃহীত করণীয়কে Assigned with dates, Needs an owner এবং Needs a date এ ভাগ করে। প্রতি আইটেমে কাজ, দায়িত্বশীল/তারিখের অবস্থা, উদ্ধৃতি ও উৎসের লাইন থাকে। এটি action-register-<payload-hash-prefix>.md ও পাশের .receipt.json লেখে, যার মধ্যে রান, উৎস, প্রার্থী ও প্রতিবেদনের হ্যাশ। দুটো খুলুন, উদ্ধৃত করণীয় নোটের সঙ্গে মিলিয়ে report_sha256 কে Markdown ফাইলের বাইট দিয়ে যাচাই করুন। বৈধ রসিদ শুধু স্থানীয় ফাইল তৈরি ও পুনরায় পড়া বোঝায়; বাইরের কাজের ব্যবস্থায় লেখার অনুমোদন নয়।
ব্যবহারের কমান্ড প্রতিবেদন ও রসিদের বাস্তব পথ দেখায়। সেই পথ দিয়ে বাইট পরীক্ষা করুন:
python -c "from pathlib import Path; import hashlib,json,sys; report=Path(sys.argv[1]); receipt=json.loads(Path(sys.argv[2]).read_text(encoding='utf-8')); assert hashlib.sha256(report.read_bytes()).hexdigest()==receipt['report_sha256']; print('report hash matches receipt')" PATH-PRINTED-FOR-REPORT PATH-PRINTED-FOR-RECEIPTপ্রার্থী পাওয়ার পরে স্পষ্টভাবে কৃত্রিম কাঠামোগত নমুনা দিয়ে প্রত্যাখ্যান পরীক্ষা করুন। JSON আলাদা ফাইলে কপি করে প্রথম করণীয়র একটি আবশ্যক ঘর সরিয়ে consume চালান সেই ভুল ফাইল ও নতুন ফাঁকা আউটপুট ডিরেক্টরি দিয়ে। ফাইল তৈরির আগেই ব্যর্থ হতে হবে। নমুনাকে তথ্যভিত্তিক নিষ্কাশন ভাববেন না বা অনুমোদন দেবেন না। যেমন নিচের স্থানীয় পরীক্ষাটি কপি থেকে evidence_quote সরায়:
python -c "import json; from pathlib import Path; p=json.loads(Path('candidate/actions.json').read_text(encoding='utf-8')); p['actions'][0].pop('evidence_quote'); Path('candidate/SYNTHETIC-malformed.json').write_text(json.dumps(p), encoding='utf-8')"
python action_register_recipe.py consume --source-id OWNED-ID --source notes.txt --candidate candidate/SYNTHETIC-malformed.json --approval approval.json --out synthetic-rejection-outputদ্বিতীয় কমান্ড ব্যর্থ হবে এবং আউটপুট ডিরেক্টরি থাকবে না। বাস্তব প্রার্থীর প্রথম করণীয় না থাকলে নমুনা ব্যবহার করবেন না; সেটি ভোক্তার জন্য আগেই অসম্পূর্ণ। লেখা অনিশ্চিত হলে পুনঃচেষ্টার আগে নির্ধারিত প্রতিবেদন ও রসিদ দেখুন। বিদ্যমান একই বাইট ফের ব্যবহার করা যায়; ভিন্ন হলে সমন্বয়ের জন্য থামে। উৎস বা স্কিমা সংস্করণ বদলালে নতুন পর্যালোচনা লাগে। শুধু স্থানীয় ভোক্তার অস্পষ্ট উত্তর পেয়ে মডেল কল পুনরাবৃত্তি করবেন না।
পূর্ণ Python পদ্ধতি
এই স্ক্রিপ্ট হুবহু action_register_recipe.py এ কপি করুন:
"""Private, bounded recipe. No model call occurs unless `produce` is explicitly run."""
import argparse
import hashlib
import json
import uuid
from datetime import date
from pathlib import Path
from typing import Literal
from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr, ValidationError
class StrictModel(BaseModel):
model_config = ConfigDict(extra="forbid", strict=True)
class Action(StrictModel):
action_text: StrictStr
evidence_quote: StrictStr
line_start: StrictInt
line_end: StrictInt
owner: StrictStr | None
due_date: StrictStr | None
evidence_status: Literal["complete", "needs_review"]
class Extraction(StrictModel):
schema_version: Literal["action-register/v1"]
status: Literal["ready", "partial", "blocked"]
source_id: StrictStr
source_sha256: StrictStr
actions: list[Action]
gap_note: StrictStr | None
def digest(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def canonical(payload: Extraction) -> bytes:
return json.dumps(payload.model_dump(mode="json"), sort_keys=True, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
def check(payload: Extraction, source_id: str, source: bytes) -> None:
if payload.source_id != source_id or payload.source_sha256 != digest(source):
raise ValueError("source identity or revision mismatch")
lines = source.decode("utf-8").splitlines()
if payload.status == "ready" and (not payload.actions or payload.gap_note is not None):
raise ValueError("ready requires actions and no gap")
if payload.status != "ready":
raise ValueError("partial or blocked result cannot reach consumer")
for index, item in enumerate(payload.actions, 1):
if item.evidence_status != "complete":
raise ValueError(f"action {index}: evidence needs review")
if not item.action_text.strip() or not item.evidence_quote.strip():
raise ValueError(f"action {index}: empty task or evidence")
if not 1 <= item.line_start <= item.line_end <= len(lines):
raise ValueError(f"action {index}: invalid source line range")
segment = "\n".join(lines[item.line_start - 1:item.line_end])
if item.evidence_quote not in segment:
raise ValueError(f"action {index}: quotation absent from source span")
if item.owner is not None and not item.owner.strip():
raise ValueError(f"action {index}: blank owner")
if item.due_date is not None:
try:
if date.fromisoformat(item.due_date).isoformat() != item.due_date:
raise ValueError()
except ValueError as exc:
raise ValueError(f"action {index}: due date must be YYYY-MM-DD") from exc
def report(payload: Extraction) -> str:
"""Application consumer: a triage register grouped by missing ownership/date."""
groups = [
("Assigned with dates", lambda x: x.owner is not None and x.due_date is not None),
("Needs an owner", lambda x: x.owner is None),
("Needs a date", lambda x: x.owner is not None and x.due_date is None),
]
rows = ["# Action register for " + payload.source_id, "", "Review artifact; source meaning was approved separately.", ""]
for heading, predicate in groups:
rows += ["## " + heading, ""]
matches = [(n, a) for n, a in enumerate(payload.actions, 1) if predicate(a)]
for number, action in matches:
rows += [f"{number}. {action.action_text}", f" Owner: {action.owner or 'Unspecified'}; due: {action.due_date or 'Unspecified'}", f" Source lines {action.line_start}-{action.line_end}: {action.evidence_quote}", ""]
if not matches:
rows += ["None.", ""]
return "\n".join(rows)
def load_payload(path: Path) -> Extraction:
return Extraction.model_validate_json(path.read_bytes(), strict=True)
def consume(source_id: str, source_path: Path, payload_path: Path, approval_path: Path, out_dir: Path) -> tuple[Path, Path]:
source = source_path.read_bytes()
payload = load_payload(payload_path)
check(payload, source_id, source)
run = json.loads(payload_path.with_suffix(".run.json").read_text(encoding="utf-8"))
payload_hash = digest(canonical(payload))
if run.get("source_id") != source_id or run.get("source_sha256") != digest(source) or run.get("payload_sha256") != payload_hash or run.get("last_agent") != "Owned-source action extractor" or not str(run.get("run_id", "")).strip():
raise ValueError("producer receipt does not match accepted candidate")
approval = json.loads(approval_path.read_text(encoding="utf-8"))
if (type(approval) is not dict
or set(approval) != {"approved", "run_id", "source_id", "source_sha256", "payload_sha256", "reviewer", "review_note"}
or approval["approved"] is not True
or any(type(approval[key]) is not str for key in ("run_id", "source_id", "source_sha256", "payload_sha256", "reviewer", "review_note"))
or not approval["reviewer"].strip() or not approval["review_note"].strip()
or approval["run_id"] != run["run_id"]
or approval["source_id"] != source_id
or approval["source_sha256"] != digest(source)
or approval["payload_sha256"] != payload_hash):
raise ValueError("human source/meaning approval must bind exact source and payload hashes")
body = report(payload).encode("utf-8")
stem = "action-register-" + payload_hash[:16]
out_dir.mkdir(parents=True, exist_ok=True)
target = out_dir / (stem + ".md")
receipt = out_dir / (stem + ".receipt.json")
record = {"run_id": run["run_id"], "source_id": source_id, "source_sha256": digest(source), "payload_sha256": payload_hash, "report_sha256": digest(body), "reviewer": approval["reviewer"], "operation": "local action-triage register"}
encoded = (json.dumps(record, indent=2) + "\n").encode("utf-8")
for path, data in [(target, body), (receipt, encoded)]:
if path.exists():
if path.read_bytes() != data:
raise ValueError(f"uncertain prior output differs: {path}; inspect before retry")
else:
with path.open("xb") as handle:
handle.write(data)
if path.read_bytes() != data:
raise IOError(f"readback failed: {path}")
return target, receipt
def produce(source_id: str, source_path: Path, candidate_path: Path, model: str) -> None:
from agents import Agent, Runner
source = source_path.read_bytes()
source_text = source.decode("utf-8")
if not source_text.strip() or candidate_path.exists() or not candidate_path.parent.is_dir():
raise ValueError("source must contain text; candidate must be new and its parent directory must already exist")
run_id = str(uuid.uuid4())
agent = Agent(
name="Owned-source action extractor",
model=model,
instructions=("Extract only explicit action commitments from the supplied numbered source. "
"Return action-register/v1. Copy an exact quote with matching 1-based line span. "
"Use null for an unstated owner or due date; never infer them. "
"Use partial and needs_review for ambiguous evidence; blocked for unusable input. "
"Do not use tools or hand off."),
output_type=Extraction,
)
numbered = "\n".join(f"{n}: {line}" for n, line in enumerate(source_text.splitlines(), 1))
try:
result = Runner.run_sync(agent, f"Source ID: {source_id}\nSHA-256: {digest(source)}\nNumbered source:\n{numbered}")
if result.interruptions or result.last_agent is not agent or not isinstance(result.final_output, Extraction):
raise ValueError("run interrupted, unfinished, or finished by unexpected agent; this CLI does not persist resumable RunState, so do not resume from its failure receipt")
output = result.final_output
if output.source_id != source_id or output.source_sha256 != digest(source):
raise ValueError("producer returned wrong source identity")
candidate = canonical(output)
with candidate_path.open("xb") as handle:
handle.write(candidate) # Candidate only; consumer still requires review.
receipt = {"run_id": run_id, "source_id": source_id, "source_sha256": digest(source), "payload_sha256": digest(candidate), "last_agent": result.last_agent.name, "model": model}
with candidate_path.with_suffix(".run.json").open("x", encoding="utf-8") as handle:
handle.write(json.dumps(receipt, indent=2) + "\n")
except Exception as exc:
failure = {"run_id": run_id, "source_id": source_id, "source_sha256": digest(source), "error_type": type(exc).__name__, "error": str(exc), "state": "unknown_or_blocked; inspect run and destination before retry"}
with candidate_path.with_name(candidate_path.stem + "." + run_id + ".failure.json").open("x", encoding="utf-8") as handle:
handle.write(json.dumps(failure, indent=2) + "\n")
raise
def main() -> None:
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest="command", required=True)
for name in ("produce", "consume"):
p = sub.add_parser(name)
p.add_argument("--source-id", required=True)
p.add_argument("--source", type=Path, required=True)
if name == "produce":
p.add_argument("--candidate", type=Path, required=True)
p.add_argument("--model", required=True)
else:
p.add_argument("--candidate", type=Path, required=True)
p.add_argument("--approval", type=Path, required=True)
p.add_argument("--out", type=Path, required=True)
args = parser.parse_args()
if args.command == "produce":
produce(args.source_id, args.source, args.candidate, args.model)
else:
print(*consume(args.source_id, args.source, args.candidate, args.approval, args.out), sep="\n")
if __name__ == "__main__":
main()সীমা ও উৎস
স্ক্রিপ্টের চুক্তি ও স্থানীয় ভোক্তা পাঁচটি অফলাইন পরীক্ষা এবং সাতটি কৃত্রিম অনুকরণমূলক উৎপাদক বা পুনঃচেষ্টা ক্ষেত্রে যাচাই হয়েছে। কঠোর স্কিমা, ফাইল তৈরির আগে প্রত্যাখ্যান, দলবদ্ধ ফল, হ্যাশ ও ব্যর্থতা সামলানো পরীক্ষা হয়েছে। পরিষেবাদাতায় স্কিমা পাঠানো, মডেল কল, বাস্তব নোট নিষ্কাশন, তথ্যগত মানব অনুমোদন, বাস্তব উৎসের প্রতিবেদন বা বিল ও মান মাপা হয়নি। API খরচ অ্যাকাউন্টভিত্তিক; উৎস পাঠানোর আগে বর্তমান সামঞ্জস্য, মূল্য ও তথ্যের অনুমতি দেখুন।
সম্পর্কিত প্রেক্ষাপটে BIG CHANGE-এর নিজের কাজ থেকে AI কাজ বাছাইয়ের নির্দেশিকা কোন কাজ পরীক্ষা করবেন সেই আগের সিদ্ধান্ত ব্যাখ্যা করে। এই নির্দেশিকা নির্বাচিত অনুমোদিত নিষ্কাশন থেকে শুরু করে নির্দিষ্ট টাইপের ফলকে যাচাই করা স্থানীয় ভোক্তায় দেয়।
উৎস ও আরও পড়ুন
- OpenAI Agents SDK: Agents Pydantic-এর
output_typeব্যাখ্যা করে। এই পদ্ধতির মানব অনুমোদন বা স্থানীয় ভোক্তা দেয় না। - OpenAI Agents SDK: Results ব্যাখ্যা করে
final_output,last_agent, বাধা এবংto_state()। এই CLI পুনরায় চালু করার অবস্থা রাখে না। - OpenAI Agents SDK: Quickstart ইনস্টল ও API কী সেটআপ ব্যাখ্যা করে। কী উৎস ও রসিদ থেকে আলাদা রাখুন।
- OpenAI Structured Outputs কঠোর স্কিমা নিয়ম ও মডেল সমর্থন ব্যাখ্যা করে। বাছা মডেল সমর্থন করে কি না নিশ্চিত করুন।
- Pydantic ConfigDict কঠোর যাচাই ও অতিরিক্ত ঘর নিষিদ্ধ করার নথি দেয়।
- OpenAI Agents SDK 0.24.0 এবং Pydantic 2.14.1 স্থানীয় ইনস্টল করা স্থির সংস্করণ চিহ্নিত করে। পরিষেবাদাতার গ্রহণ পরীক্ষা হয়নি।



