CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-domain

Core domain type definitions for the Embabel Agent Framework, providing foundational data classes and interfaces for agent-based AI workflows including content assets, research entities, and person types with Jackson serialization and PromptContributor capabilities.

Overview
Eval results
Files

integration-patterns.mddocs/

Integration Patterns

Common patterns for using domain types together in agent workflows.

Content Asset Polymorphism

import com.embabel.agent.domain.library.*

// Function accepting any ContentAsset
fun processContentAsset(asset: ContentAsset) {
    println("Content: ${asset.content.take(100)}...")
    println("Timestamp: ${asset.timestamp}")
    println("For LLM: ${asset.contribution()}")
}

// Works with Blog
val blog = Blog("Title", "Author", "Content")
processContentAsset(blog)

// Works with ResearchReport
val report = ResearchReport("Topic", "Content", listOf())
processContentAsset(report)

// Collections
val assets: List<ContentAsset> = listOf(
    Blog("Blog Title", "Author", "Blog content"),
    ResearchReport("Research Topic", "Research content", listOf())
)

assets.forEach { processContentAsset(it) }

HasContent Polymorphism

import com.embabel.agent.domain.library.*

// Function accepting any HasContent
fun extractText(item: HasContent): String {
    return item.content
}

// Works with multiple types
extractText(Summary("Summary text"))
extractText(Blog("Title", "Author", "Blog content"))
extractText(ResearchReport("Topic", "Report content", listOf()))

Blog from Research

import com.embabel.agent.domain.library.*

fun createBlogFromResearch(
    report: ResearchReport,
    author: String
): Blog {
    val linksSection = report.links.joinToString("\n") { link ->
        "- [${link.summary}](${link.url})"
    }

    val content = """
        ${report.content}

        ## References
        $linksSection
    """.trimIndent()

    return Blog(
        title = report.topic,
        author = author,
        content = content,
        keywords = setOf("research", report.topic.lowercase())
    )
}

// Usage
val research = ResearchReport(
    topic = "AI Ethics",
    content = "AI ethics encompasses...",
    links = listOf(
        InternetResource(
            url = "https://example.com/ethics",
            summary = "Ethics guide"
        )
    )
)

val blog = createBlogFromResearch(research, "Dr. Smith")

Blog from News

import com.embabel.agent.domain.library.*

fun createBlogFromNews(
    news: RelevantNewsStories,
    author: String,
    title: String = "This Week in AI"
): Blog {
    val newsContent = news.items.joinToString("\n\n") { story ->
        "## ${story.title}\n\n${story.summary}\n\n[Read more](${story.url})"
    }

    return Blog(
        title = title,
        author = author,
        content = "# Latest News\n\n$newsContent",
        keywords = setOf("news", "ai", "weekly-roundup")
    )
}

// Usage
val news = RelevantNewsStories(
    items = listOf(
        NewsStory(
            "https://ex.com/1",
            "AI Breakthrough",
            "New algorithm..."
        ),
        NewsStory(
            "https://ex.com/2",
            "Industry Adoption",
            "Companies embrace..."
        )
    )
)

val blog = createBlogFromNews(news, "News Editor")

Summary from Content Assets

import com.embabel.agent.domain.library.*

fun summarizeContentAsset(asset: ContentAsset): Summary {
    return when (asset) {
        is Blog -> Summary(
            "Blog '${asset.title}' by ${asset.author}: ${asset.content.take(100)}..."
        )
        is ResearchReport -> Summary(
            "Research on '${asset.topic}': ${asset.content.take(200)}... " +
            "References ${asset.links.size} sources."
        )
        else -> Summary(
            "Content from ${asset.timestamp}: ${asset.content.take(100)}..."
        )
    }
}

// Usage
val blog = Blog("Title", "Author", "Long content...")
val blogSummary = summarizeContentAsset(blog)

val report = ResearchReport("Topic", "Long report...", listOf())
val reportSummary = summarizeContentAsset(report)

Research with News Context

import com.embabel.agent.domain.library.*

fun enrichResearchWithNews(
    report: ResearchReport,
    news: RelevantNewsStories
): String {
    return """
        ${report.contribution()}

        ## Recent News
        ${news.contribution()}
    """.trimIndent()
}

// Usage in LLM prompt
val report = ResearchReport(
    topic = "AI Agent Frameworks",
    content = "Frameworks landscape...",
    links = listOf()
)

val news = RelevantNewsStories(
    items = listOf(
        NewsStory(
            "https://ex.com/news",
            "Framework Update",
            "New version released..."
        )
    )
)

val enrichedContext = enrichResearchWithNews(report, news)

val prompt = """
    Context:
    $enrichedContext

    Based on this, recommend...
""".trimIndent()

Person with Content

import com.embabel.agent.domain.library.*

data class BlogAuthor(
    override val name: String,
    val bio: String,
    val expertise: List<String>
) : Person

data class ContentWithAuthor(
    val content: ContentAsset,
    val author: Person
)

fun createBlogWithAuthor(
    author: BlogAuthor,
    title: String,
    content: String
): ContentWithAuthor {
    val blog = Blog(
        title = title,
        author = author.name,
        content = content,
        keywords = author.expertise.map { it.lowercase() }.toSet()
    )

    return ContentWithAuthor(blog, author)
}

// Usage
val author = BlogAuthor(
    name = "Dr. Jane Smith",
    bio = "AI researcher",
    expertise = listOf("AI", "Machine Learning", "Ethics")
)

val contentWithAuthor = createBlogWithAuthor(
    author = author,
    title = "AI Ethics Principles",
    content = "Ethics are crucial..."
)

Multi-type LLM Prompt

import com.embabel.agent.domain.library.*

data class AgentContext(
    val research: ResearchReport?,
    val news: RelevantNewsStories?,
    val relatedBlogs: List<Blog>,
    val summary: Summary?
)

fun buildContextPrompt(context: AgentContext): String {
    val parts = mutableListOf<String>()

    context.research?.let {
        parts.add("# Research\n${it.contribution()}")
    }

    context.news?.let {
        if (it.items.isNotEmpty()) {
            parts.add("# Recent News\n${it.contribution()}")
        }
    }

    if (context.relatedBlogs.isNotEmpty()) {
        val blogs = context.relatedBlogs.joinToString("\n\n") { it.contribution() }
        parts.add("# Related Content\n$blogs")
    }

    context.summary?.let {
        parts.add("# Summary\n${it.content}")
    }

    return parts.joinToString("\n\n")
}

// Usage
val context = AgentContext(
    research = ResearchReport("Topic", "Content...", listOf()),
    news = RelevantNewsStories(listOf(
        NewsStory("https://ex.com", "Title", "Summary")
    )),
    relatedBlogs = listOf(
        Blog("Related Post", "Author", "Content...")
    ),
    summary = Summary("Context summary...")
)

val prompt = """
    ${buildContextPrompt(context)}

    Based on this context, analyze...
""".trimIndent()

Research Workflow

import com.embabel.agent.domain.library.*

data class ResearchWorkflow(
    val topics: ResearchTopics,
    val reports: List<ResearchReport>,
    val news: RelevantNewsStories,
    val summaries: List<Summary>
)

fun conductResearch(topics: ResearchTopics): ResearchWorkflow {
    // 1. Collect news for each topic
    val allNews = mutableListOf<NewsStory>()
    topics.topics.forEach { topic ->
        // In real agent: search news for topic.topic
        allNews.add(NewsStory(
            url = "https://ex.com/${topic.topic}",
            title = "News about ${topic.topic}",
            summary = "Latest developments..."
        ))
    }

    // 2. Create research reports
    val reports = topics.topics.map { topic ->
        ResearchReport(
            topic = topic.topic,
            content = "Research findings for ${topic.topic}...",
            links = listOf(
                InternetResource(
                    url = "https://ex.com/${topic.topic}/research",
                    summary = "Research source"
                )
            )
        )
    }

    // 3. Create summaries
    val summaries = reports.map { report ->
        Summary("Summary of ${report.topic}: ${report.content.take(100)}...")
    }

    return ResearchWorkflow(
        topics = topics,
        reports = reports,
        news = RelevantNewsStories(allNews),
        summaries = summaries
    )
}

// Usage
val topics = ResearchTopics(
    topics = listOf(
        ResearchTopic(
            topic = "AI Ethics",
            questions = listOf("What are key concerns?")
        ),
        ResearchTopic(
            topic = "Agent Frameworks",
            questions = listOf("What frameworks exist?")
        )
    )
)

val workflow = conductResearch(topics)

Content Aggregation

import com.embabel.agent.domain.library.*

class ContentAggregator {
    private val blogs = mutableListOf<Blog>()
    private val reports = mutableListOf<ResearchReport>()
    private val news = mutableListOf<NewsStory>()

    fun addBlog(blog: Blog) {
        blogs.add(blog)
    }

    fun addReport(report: ResearchReport) {
        reports.add(report)
    }

    fun addNews(story: NewsStory) {
        news.add(story)
    }

    fun getAllContent(): List<ContentAsset> {
        return blogs + reports
    }

    fun buildPrompt(): String {
        val sections = mutableListOf<String>()

        if (reports.isNotEmpty()) {
            sections.add("# Research Reports\n" +
                reports.joinToString("\n\n") { it.contribution() }
            )
        }

        if (blogs.isNotEmpty()) {
            sections.add("# Blog Posts\n" +
                blogs.joinToString("\n\n") { it.contribution() }
            )
        }

        if (news.isNotEmpty()) {
            sections.add("# News Stories\n" +
                RelevantNewsStories(news).contribution()
            )
        }

        return sections.joinToString("\n\n")
    }

    fun getSummary(): Summary {
        return Summary(
            "Aggregated ${blogs.size} blogs, ${reports.size} reports, " +
            "${news.size} news stories"
        )
    }
}

// Usage
val aggregator = ContentAggregator()

aggregator.addBlog(Blog("Title 1", "Author 1", "Content 1"))
aggregator.addReport(ResearchReport("Topic 1", "Content 1", listOf()))
aggregator.addNews(NewsStory("https://ex.com", "News 1", "Summary 1"))

val prompt = aggregator.buildPrompt()
val summary = aggregator.getSummary()

Time-based Filtering

import com.embabel.agent.domain.library.*
import java.time.Instant
import java.time.temporal.ChronoUnit

fun filterRecentContent(
    assets: List<ContentAsset>,
    daysBack: Long = 7
): List<ContentAsset> {
    val cutoff = Instant.now().minus(daysBack, ChronoUnit.DAYS)
    return assets.filter { it.timestamp.isAfter(cutoff) }
}

fun groupByTimeRange(
    assets: List<ContentAsset>
): Map<String, List<ContentAsset>> {
    val now = Instant.now()
    val oneDayAgo = now.minus(1, ChronoUnit.DAYS)
    val oneWeekAgo = now.minus(7, ChronoUnit.DAYS)

    return mapOf(
        "today" to assets.filter { it.timestamp.isAfter(oneDayAgo) },
        "this_week" to assets.filter {
            it.timestamp.isAfter(oneWeekAgo) && it.timestamp.isBefore(oneDayAgo)
        },
        "older" to assets.filter { it.timestamp.isBefore(oneWeekAgo) }
    )
}

// Usage
val assets: List<ContentAsset> = listOf(
    Blog("Recent", "Author", "Content"),
    Blog("Old", "Author", "Content",
        timestamp = Instant.now().minus(30, ChronoUnit.DAYS)),
    ResearchReport("Recent Research", "Content", listOf())
)

val recent = filterRecentContent(assets, daysBack = 7)
val grouped = groupByTimeRange(assets)

Keyword-based Relationships

import com.embabel.agent.domain.library.*

fun findRelatedBlogs(
    targetBlog: Blog,
    allBlogs: List<Blog>,
    minKeywordOverlap: Int = 1
): List<Blog> {
    return allBlogs
        .filter { it != targetBlog }
        .filter { blog ->
            val overlap = blog.keywords.intersect(targetBlog.keywords)
            overlap.size >= minKeywordOverlap
        }
        .sortedByDescending { blog ->
            blog.keywords.intersect(targetBlog.keywords).size
        }
}

fun createRelatedContentPrompt(
    mainContent: Blog,
    related: List<Blog>
): String {
    return """
        Main Content:
        ${mainContent.contribution()}

        Related Content:
        ${related.joinToString("\n\n") { it.contribution() }}
    """.trimIndent()
}

// Usage
val mainBlog = Blog(
    title = "AI Ethics",
    author = "Author",
    content = "Ethics content...",
    keywords = setOf("ai", "ethics", "fairness")
)

val allBlogs = listOf(
    Blog("ML Fairness", "Author", "Content",
        keywords = setOf("ai", "fairness", "ml")),
    Blog("Unrelated Topic", "Author", "Content",
        keywords = setOf("databases", "sql"))
)

val related = findRelatedBlogs(mainBlog, allBlogs)
val prompt = createRelatedContentPrompt(mainBlog, related)

Best Practices

  1. Use interface types - Accept ContentAsset, HasContent, Person for flexibility
  2. Leverage contribution() - Use for consistent LLM formatting
  3. Filter by timestamp - ContentAssets have timestamps for time-based filtering
  4. Combine types - Mix blogs, research, news for rich context
  5. Build hierarchies - Create summaries of content collections
  6. Extend types - Use open classes to add custom properties
  7. Type-safe polymorphism - Use when expressions with is checks
tessl i tessl/maven-com-embabel-agent--embabel-agent-domain@0.3.0

docs

content-types.md

core-concepts.md

external-types.md

index.md

installation.md

integration-patterns.md

json-serialization.md

news-types.md

person-types.md

quick-reference.md

research-types.md

summary-type.md

tile.json