Add agent.py

This commit is contained in:
2026-06-02 14:56:37 +00:00
parent de379922a5
commit 3b760b5103
+173
View File
@@ -0,0 +1,173 @@
"""CLI tool that extracts structured data from text using LangChain and Pydantic.
The tool supports two schemas:
- PersonInfo
- MeetingNotes
It automatically detects which schema to use based on the input text.
"""
import sys
import argparse
from typing import List
from dotenv import load_dotenv
import os
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
# Load environment variables
load_dotenv()
# LLM configuration user can override via env vars
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "")
# ---------- Pydantic models ----------
class PersonInfo(BaseModel):
"""Information about a person."""
name: str = Field(..., description="Person's name")
age: int = Field(..., description="Age of the person")
profession: str = Field(..., description="Profession of the person")
skills: List[str] = Field(..., description="List of skills")
class MeetingNotes(BaseModel):
"""Notes from a meeting."""
title: str = Field(..., description="Title of the meeting")
date: str = Field(..., description="Date of the meeting")
participants: List[str] = Field(..., description="List of participants")
agenda: List[str] = Field(..., description="Agenda items")
# ---------- LLM instance ----------
llm = ChatOpenAI(
model=OPENAI_MODEL,
api_key=OPENAI_API_KEY,
base_url=OPENAI_BASE_URL if OPENAI_BASE_URL else None,
temperature=0,
)
# ---------- Prompts and parsers ----------
# 1. Schema detection prompt
schema_detection_prompt = PromptTemplate(
input_variables=["text"],
template="""
Determine whether the following text is about a person or a meeting. Respond with one of the words:
- PERSON
- MEETING
Text: {text}
Answer:
"""
)
# 2. PersonInfo extraction prompt
person_prompt = PromptTemplate(
input_variables=["text"],
template="""
Extract the following information about a person from the text:
- name
- age
- profession
- skills (comma separated list)
Return a JSON object with fields name, age, profession, skills.
Text: {text}
JSON:
"""
)
person_parser = PydanticOutputParser(pydantic_object=PersonInfo)
person_chain = person_prompt | llm | person_parser
# 3. MeetingNotes extraction prompt
meeting_prompt = PromptTemplate(
input_variables=["text"],
template="""
Extract the following information about a meeting from the text:
- title
- date
- participants (comma separated list)
- agenda (comma separated list)
Return a JSON object with fields title, date, participants, agenda.
Text: {text}
JSON:
"""
)
meeting_parser = PydanticOutputParser(pydantic_object=MeetingNotes)
meeting_chain = meeting_prompt | llm | meeting_parser
# ---------- Helper functions ----------
def detect_schema(text: str) -> str:
"""Return "PERSON" or "MEETING" based on LLM classification."""
try:
result = schema_detection_prompt.invoke({"text": text}, llm=llm)
# The result is a string; strip and uppercase
return result.strip().upper()
except Exception as e:
raise RuntimeError(f"Schema detection failed: {e}")
def extract_person(text: str) -> PersonInfo:
"""Run the person extraction chain."""
try:
return person_chain.invoke({"text": text})
except Exception as e:
raise RuntimeError(f"Person extraction failed: {e}")
def extract_meeting(text: str) -> MeetingNotes:
"""Run the meeting extraction chain."""
try:
return meeting_chain.invoke({"text": text})
except Exception as e:
raise RuntimeError(f"Meeting extraction failed: {e}")
# ---------- CLI ----------
def main() -> None:
parser = argparse.ArgumentParser(description="Extract structured data from text.")
parser.add_argument(
"text",
nargs="?",
help="Input text. If omitted, read from stdin.",
)
args = parser.parse_args()
if args.text:
input_text = args.text
else:
input_text = sys.stdin.read().strip()
if not input_text:
print("No input provided.", file=sys.stderr)
sys.exit(1)
# Detect schema
schema_type = detect_schema(input_text)
if schema_type == "PERSON":
obj = extract_person(input_text)
elif schema_type == "MEETING":
obj = extract_meeting(input_text)
else:
print(f"Unable to determine schema type: {schema_type}", file=sys.stderr)
sys.exit(1)
# Output
print(obj.model_dump(indent=2))
# Simple summary
if isinstance(obj, PersonInfo):
summary = f"{obj.name}, {obj.age} years old, works as {obj.profession}.\n" + f"Skills: {', '.join(obj.skills)}."
else:
summary = f"Meeting '{obj.title}' on {obj.date} with participants {', '.join(obj.participants)}. Agenda: {', '.join(obj.agenda)}."
print("\nSummary:\n" + summary)
if __name__ == "__main__":
main()