Skip to content

Commit 8e99e5d

Browse files
committed
gen: (GPT-5) news summarizer agent で実装されている Scraper と Summarizer の抽象クラスを作成してリファクタしてください
1 parent 0f6d8bc commit 8e99e5d

File tree

4 files changed

+136
-55
lines changed

4 files changed

+136
-55
lines changed

template_langgraph/agents/image_classifier_agent/classifiers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def predict(self, prompt: str, image: str, llm: Any) -> Result: # noqa: D401
6060
class LlmClassifier(BaseClassifier):
6161
"""LLM-backed classifier using the provided model's structured output capability."""
6262

63-
def predict(self, prompt: str, image: str, llm: BaseChatModel) -> Result: # noqa: D401
63+
def predict(self, prompt: str, image: str, llm: BaseChatModel):
6464
logger.info(f"Classifying image with LLM: {prompt}")
6565
return llm.with_structured_output(Result).invoke(
6666
input=[

template_langgraph/agents/news_summarizer_agent/agent.py

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
StructuredArticle,
99
SummarizeWebContentState,
1010
)
11+
from template_langgraph.agents.news_summarizer_agent.scrapers import (
12+
BaseScraper,
13+
HttpxScraper,
14+
MockScraper,
15+
)
16+
from template_langgraph.agents.news_summarizer_agent.summarizers import (
17+
BaseSummarizer,
18+
LlmSummarizer,
19+
MockSummarizer,
20+
)
1121
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
1222
from template_langgraph.loggers import get_logger
1323

@@ -20,68 +30,18 @@ def notify(self, id: str, body: dict) -> None:
2030
logger.info(f"Notification sent for request {id}: {body}")
2131

2232

23-
class MockScraper:
24-
def scrape(self, url: str) -> str:
25-
"""Simulate scraping a web page."""
26-
return "<html><body><h1>Mocked web content</h1></body></html>"
27-
28-
29-
class HttpxScraper:
30-
def scrape(self, url: str) -> str:
31-
"""Retrieve the HTML content of a web page."""
32-
with httpx.Client() as client:
33-
response = client.get(url)
34-
response.raise_for_status()
35-
return response.text
36-
37-
38-
class MockSummarizer:
39-
def summarize(
40-
self,
41-
prompt: str,
42-
content: str,
43-
) -> StructuredArticle:
44-
"""Simulate summarizing the input."""
45-
return StructuredArticle(
46-
title="Mocked Title",
47-
date="2023-01-01",
48-
summary=f"Mocked summary of the content: {content}, prompt: {prompt}",
49-
keywords=["mock", "summary"],
50-
score=75,
51-
)
52-
53-
54-
class LlmSummarizer:
55-
def __init__(self, llm=AzureOpenAiWrapper().chat_model):
56-
self.llm = llm
57-
58-
def summarize(
59-
self,
60-
prompt: str,
61-
content: str,
62-
) -> StructuredArticle:
63-
"""Use the LLM to summarize the input."""
64-
logger.info(f"Summarizing input with LLM: {prompt}")
65-
return self.llm.with_structured_output(StructuredArticle).invoke(
66-
input=[
67-
{"role": "system", "content": prompt},
68-
{"role": "user", "content": content},
69-
]
70-
)
71-
72-
7333
class NewsSummarizerAgent:
7434
def __init__(
7535
self,
7636
llm=AzureOpenAiWrapper().chat_model,
7737
notifier=MockNotifier(),
78-
scraper=MockScraper(),
79-
summarizer=MockSummarizer(),
38+
scraper: BaseScraper = MockScraper(),
39+
summarizer: BaseSummarizer = MockSummarizer(),
8040
):
8141
self.llm = llm
8242
self.notifier = notifier
83-
self.scraper = scraper
84-
self.summarizer = summarizer
43+
self.scraper: BaseScraper = scraper
44+
self.summarizer: BaseSummarizer = summarizer
8545

8646
def create_graph(self):
8747
"""Create the main graph for the agent."""
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Scraper interfaces and implementations for NewsSummarizerAgent.
2+
3+
This module defines an abstract base scraper so different scraping strategies
4+
(mock, httpx-based, future headless browser, etc.) can be plugged into the agent
5+
without changing orchestration logic.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from abc import ABC, abstractmethod
11+
12+
import httpx
13+
14+
from template_langgraph.loggers import get_logger
15+
16+
logger = get_logger(__name__)
17+
18+
19+
class BaseScraper(ABC):
20+
"""Abstract base scraper.
21+
22+
Implementations should raise exceptions outward only when they are truly
23+
unrecoverable; transient network issues should surface as httpx.RequestError
24+
so the caller can decide how to handle them.
25+
"""
26+
27+
@abstractmethod
28+
def scrape(self, url: str) -> str: # pragma: no cover - interface
29+
"""Retrieve raw textual/HTML content for the given URL.
30+
31+
Args:
32+
url: Target URL.
33+
Returns:
34+
str: Raw content (HTML / text).
35+
"""
36+
raise NotImplementedError
37+
38+
39+
class MockScraper(BaseScraper):
40+
"""Deterministic scraper for tests / offline development."""
41+
42+
def scrape(self, url: str) -> str: # noqa: D401
43+
logger.info(f"Mock scrape for URL: {url}")
44+
return "<html><body><h1>Mocked web content</h1></body></html>"
45+
46+
47+
class HttpxScraper(BaseScraper):
48+
"""Simple httpx based scraper."""
49+
50+
def scrape(self, url: str) -> str: # noqa: D401
51+
logger.info(f"Fetching URL via httpx: {url}")
52+
with httpx.Client() as client:
53+
response = client.get(url)
54+
response.raise_for_status()
55+
return response.text
56+
57+
58+
__all__ = [
59+
"BaseScraper",
60+
"MockScraper",
61+
"HttpxScraper",
62+
]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Summarizer interfaces and implementations for NewsSummarizerAgent."""
2+
3+
from __future__ import annotations
4+
5+
from abc import ABC, abstractmethod
6+
from typing import Any
7+
8+
from langchain_core.language_models.chat_models import BaseChatModel
9+
10+
from template_langgraph.agents.news_summarizer_agent.models import StructuredArticle
11+
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
12+
from template_langgraph.loggers import get_logger
13+
14+
logger = get_logger(__name__)
15+
16+
17+
class BaseSummarizer(ABC):
18+
"""Abstract base summarizer returning a StructuredArticle."""
19+
20+
@abstractmethod
21+
def summarize(self, prompt: str, content: str) -> StructuredArticle: # pragma: no cover - interface
22+
"""Summarize raw content using a given prompt."""
23+
raise NotImplementedError
24+
25+
26+
class MockSummarizer(BaseSummarizer):
27+
"""Deterministic summarizer for tests / offline development."""
28+
29+
def summarize(self, prompt: str, content: str) -> StructuredArticle: # noqa: D401
30+
return StructuredArticle(
31+
title="Mocked Title",
32+
date="2023-01-01",
33+
summary=f"Mocked summary of the content: {content}, prompt: {prompt}",
34+
keywords=["mock", "summary"],
35+
score=75,
36+
)
37+
38+
39+
class LlmSummarizer(BaseSummarizer):
40+
"""LLM backed summarizer leveraging structured output."""
41+
42+
def __init__(self, llm: BaseChatModel | Any = AzureOpenAiWrapper().chat_model):
43+
self.llm = llm
44+
45+
def summarize(self, prompt: str, content: str) -> StructuredArticle: # noqa: D401
46+
logger.info(f"Summarizing input with LLM: {prompt}")
47+
return self.llm.with_structured_output(StructuredArticle).invoke(
48+
input=[
49+
{"role": "system", "content": prompt},
50+
{"role": "user", "content": content},
51+
]
52+
)
53+
54+
55+
__all__ = [
56+
"BaseSummarizer",
57+
"MockSummarizer",
58+
"LlmSummarizer",
59+
]

0 commit comments

Comments
 (0)