Skip to content

Commit c0d56c7

Browse files
committed
Agent creator
1 parent 520f087 commit c0d56c7

File tree

12 files changed

+441
-56
lines changed

12 files changed

+441
-56
lines changed

.embabel/coding-style.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Coding style
2+
3+
## General
4+
5+
The project uses Maven, Kotlin and Spring Boot.
6+
7+
Follow the style of the code you read. Favor clarity.
8+
9+
Don't bother putting in licence headers, as build will do that.
10+
11+
Don't comment obvious things, inline or in type headers.
12+
Comment only things that may be non-obvious. LLMs offer comment
13+
more than humans; don't do that.
14+
15+
Use consistent naming in the Spring idiom.
16+
17+
Use the Spring idiom where possible.
18+
19+
Favor immutability.
20+
21+
Use the `Schema` and related annotations to add information to types passed over the wire in the REST application.
22+
This will improve Swagger/OpenAPI documentation.
23+
24+
Unless there is a specific reason not to, use the latest GA version of all dependencies.
25+
26+
Use @Nested classes in tests. Use `test complicated thing` instead of @DisplayName for test cases.
27+
28+
In log statements, use placeholders for efficiency at all logging levels.
29+
E.g. logger.info("{} {}", a, b) instead of logger.info("computed string").
30+
31+
## Java
32+
33+
- Use modern Java features like var, records, and enhanced switch expressions.
34+
- Use multiline strings rather than concatenation.

pom.xml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
<properties>
2020
<java.version>21</java.version>
21-
<embabel-agent.version>0.1.1</embabel-agent.version>
21+
<embabel-agent.version>0.1.2-SNAPSHOT</embabel-agent.version>
2222
</properties>
2323

2424
<dependencies>
@@ -30,11 +30,16 @@
3030
<version>${embabel-agent.version}</version>
3131
</dependency>
3232

33+
<dependency>
34+
<groupId>com.embabel.agent</groupId>
35+
<artifactId>embabel-agent-code</artifactId>
36+
<version>${embabel-agent.version}</version>
37+
</dependency>
38+
3339
<dependency>
3440
<groupId>com.embabel.agent</groupId>
3541
<artifactId>embabel-agent-test</artifactId>
36-
<!-- This is later than agent version, but will come back in sync in future -->
37-
<version>0.1.2-SNAPSHOT</version>
42+
<version>${embabel-agent.version}</version>
3843
<scope>test</scope>
3944
</dependency>
4045

@@ -61,20 +66,21 @@
6166
<repository>
6267
<id>embabel-releases</id>
6368
<url>https://repo.embabel.com/artifactory/libs-release</url>
69+
<releases>
70+
<enabled>true</enabled>
71+
</releases>
6472
<snapshots>
6573
<enabled>false</enabled>
6674
</snapshots>
6775
</repository>
6876
<repository>
6977
<id>embabel-snapshots</id>
7078
<url>https://repo.embabel.com/artifactory/libs-snapshot</url>
71-
<releases>
72-
<enabled>false</enabled>
73-
</releases>
7479
<snapshots>
7580
<enabled>true</enabled>
7681
</snapshots>
7782
</repository>
7883
</repositories>
7984

85+
8086
</project>

src/main/java/com/embabel/template/DemoShell.java renamed to src/main/java/com/embabel/DemoShell.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.embabel.template;
1+
package com.embabel;
22

33
import com.embabel.template.injected.InjectedDemo;
44
import org.springframework.shell.standard.ShellComponent;

src/main/java/com/embabel/ProjectNameApplication.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
import com.embabel.agent.config.annotation.EnableAgentShell;
1919
import com.embabel.agent.config.annotation.EnableAgents;
2020
import com.embabel.agent.config.annotation.LoggingThemes;
21+
import com.embabel.coding.Tyrell;
2122
import org.springframework.boot.SpringApplication;
2223
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2325

2426

2527
@SpringBootApplication
2628
@EnableAgentShell
29+
@EnableConfigurationProperties(Tyrell.Config.class)
2730
@EnableAgents(loggingTheme = LoggingThemes.STAR_WARS)
2831
class ProjectNameApplication {
2932
public static void main(String[] args) {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.embabel.coding;
2+
3+
import com.embabel.agent.api.annotation.*;
4+
import com.embabel.agent.api.common.OperationContext;
5+
import com.embabel.agent.api.common.ToolObject;
6+
import com.embabel.agent.domain.library.code.SoftwareProject;
7+
import com.embabel.agent.tools.common.LlmReference;
8+
import com.embabel.coding.tools.api.ApiReference;
9+
import com.embabel.coding.tools.git.RepositoryReferenceProvider;
10+
import com.embabel.coding.tools.jvm.ClassGraphApiReferenceExtractor;
11+
import com.embabel.common.ai.model.LlmOptions;
12+
import org.springframework.boot.context.properties.ConfigurationProperties;
13+
14+
import java.util.LinkedList;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Set;
18+
19+
20+
@Agent(description = "Agent that creates other agents")
21+
public class Tyrell {
22+
23+
private final Config config;
24+
25+
private final List<LlmReference> references = new LinkedList<>();
26+
27+
private final SoftwareProject softwareProject = new SoftwareProject(System.getProperty("user.dir"));
28+
29+
public Tyrell(Config config) {
30+
this.config = config;
31+
var embabelApiReference = new ApiReference(
32+
new ClassGraphApiReferenceExtractor().fromProjectClasspath(
33+
"embabel-agent",
34+
Set.of("com.embabel.agent"),
35+
Set.of()),
36+
100);
37+
var examplesReference = RepositoryReferenceProvider.create()
38+
.cloneRepository("https://github.com/embabel/embabel-agent-examples.git");
39+
references.add(embabelApiReference);
40+
references.add(examplesReference);
41+
}
42+
43+
44+
@ConfigurationProperties(prefix = "embabel.tyrell")
45+
public record Config(LlmOptions codingLlm) {
46+
}
47+
48+
public record AgentCreationRequest(String pkg, String purpose) {
49+
}
50+
51+
/**
52+
* The agent will have been created to fulfill this summary.
53+
*
54+
* @param summary
55+
*/
56+
public record AgentCreationResult(String summary) {
57+
}
58+
59+
@Action
60+
AgentCreationRequest requestAgentDetails() {
61+
return WaitFor.formSubmission(
62+
"Please enter the package and purpose of the new agent",
63+
AgentCreationRequest.class);
64+
}
65+
66+
@Action
67+
@AchievesGoal(
68+
description = "New agent has been created",
69+
export = @Export(remote = true, startingInputTypes = {AgentCreationRequest.class}))
70+
public AgentCreationResult createAgent(AgentCreationRequest request, OperationContext embabel) {
71+
return embabel.ai()
72+
.withLlm(config.codingLlm)
73+
.withPromptElements(references)
74+
// TODO need withToolObjects method in API
75+
.withToolObject(new ToolObject(references.get(0)).withNamingStrategy(n -> "api_ref_" + n))
76+
.withToolObject(new ToolObject(references.get(1)).withNamingStrategy(n -> "examples_repo_" + n))
77+
.withToolObject(new ToolObject(softwareProject).withNamingStrategy(n -> "this_project_" + n))
78+
.withPromptElements(softwareProject)
79+
.withTemplate("coding/creator")
80+
.createObject(
81+
AgentCreationResult.class,
82+
Map.of(
83+
"package", request.pkg(),
84+
"purpose", request.purpose()
85+
));
86+
}
87+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package com.embabel.joke.agent;
2+
3+
import com.embabel.agent.api.annotation.AchievesGoal;
4+
import com.embabel.agent.api.annotation.Action;
5+
import com.embabel.agent.api.annotation.Agent;
6+
import com.embabel.agent.api.annotation.Export;
7+
import com.embabel.agent.api.common.OperationContext;
8+
import com.embabel.agent.domain.io.UserInput;
9+
import com.embabel.agent.prompt.persona.Persona;
10+
import com.embabel.agent.prompt.persona.RoleGoalBackstory;
11+
import com.embabel.common.ai.model.LlmOptions;
12+
import org.springframework.beans.factory.annotation.Value;
13+
import org.springframework.context.annotation.Profile;
14+
15+
import java.util.List;
16+
17+
abstract class JokePersonas {
18+
static final RoleGoalBackstory JOKE_WRITER = RoleGoalBackstory
19+
.withRole("Professional Comedian")
20+
.andGoal("Write funny and clever jokes that make people laugh")
21+
.andBackstory("Spent 20 years doing stand-up comedy in clubs around the world");
22+
23+
static final Persona JOKE_CRITIC = Persona.create(
24+
"Comedy Critic",
25+
"Late Night Comedy Show Writer",
26+
"Sharp, witty, and constructive",
27+
"Evaluate jokes for humor, delivery, and audience appeal"
28+
);
29+
}
30+
31+
record Joke(String setup, String punchline) {
32+
}
33+
34+
record JokeIdea(String topic, String style) {
35+
}
36+
37+
record RefinedJoke(
38+
Joke originalJoke,
39+
Joke improvedJoke,
40+
String critique
41+
) {
42+
}
43+
44+
@Agent(description = "Generate jokes based on user input and refine them")
45+
@Profile("!test")
46+
class JokeAgent {
47+
48+
private final int maxJokeLength;
49+
50+
JokeAgent(@Value("${maxJokeLength:150}") int maxJokeLength) {
51+
this.maxJokeLength = maxJokeLength;
52+
}
53+
54+
@AchievesGoal(
55+
description = "A joke has been generated and refined based on feedback",
56+
export = @Export(remote = true, name = "generateAndRefineJoke"))
57+
@Action
58+
RefinedJoke refineJoke(UserInput userInput, Joke joke, OperationContext context) {
59+
var critique = context
60+
.ai()
61+
.withAutoLlm()
62+
.withPromptContributor(JokePersonas.JOKE_CRITIC)
63+
.generateText(String.format("""
64+
Evaluate this joke. Provide constructive feedback on:
65+
- How funny it is
66+
- Whether the punchline lands well
67+
- If it's appropriate for the intended audience
68+
- Suggestions for improvement
69+
70+
Keep your critique brief and actionable.
71+
72+
# The Joke
73+
Setup: %s
74+
Punchline: %s
75+
76+
# Original user request
77+
%s
78+
""",
79+
joke.setup(),
80+
joke.punchline(),
81+
userInput.getContent()
82+
).trim());
83+
84+
var improvedJoke = context
85+
.ai()
86+
.withLlm(LlmOptions.withAutoLlm().withTemperature(.8))
87+
.withPromptContributor(JokePersonas.JOKE_WRITER)
88+
.createObject(String.format("""
89+
Based on this critique, create an improved version of the joke.
90+
Keep the total length under %d characters.
91+
92+
# Original Joke
93+
Setup: %s
94+
Punchline: %s
95+
96+
# Critique
97+
%s
98+
""",
99+
maxJokeLength,
100+
joke.setup(),
101+
joke.punchline(),
102+
critique
103+
).trim(), Joke.class);
104+
105+
return new RefinedJoke(joke, improvedJoke, critique);
106+
}
107+
108+
@Action
109+
Joke writeJoke(JokeIdea jokeIdea, OperationContext context) {
110+
return context.ai()
111+
.withLlm(LlmOptions.withAutoLlm().withTemperature(.9))
112+
.withPromptContributor(JokePersonas.JOKE_WRITER)
113+
.createObject(String.format("""
114+
Write a joke about: %s
115+
Style: %s
116+
117+
The joke should have a clear setup and punchline.
118+
Keep the total length under %d characters.
119+
Make it clever and funny.
120+
""",
121+
jokeIdea.topic(),
122+
jokeIdea.style(),
123+
maxJokeLength
124+
).trim(), Joke.class);
125+
}
126+
127+
@Action
128+
JokeIdea brainstormJoke(UserInput userInput, OperationContext context) {
129+
return context.ai()
130+
.withAutoLlm()
131+
.createObject(String.format("""
132+
Based on the user's input, identify:
133+
1. A topic for a joke (be specific)
134+
2. A comedy style (e.g., pun, observational, one-liner, knock-knock, etc.)
135+
136+
If the user didn't specify preferences, choose something appropriate and funny.
137+
138+
# User input
139+
%s
140+
""",
141+
userInput.getContent()
142+
).trim(), JokeIdea.class);
143+
}
144+
}

src/main/resources/application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
#embabel.models.embeddingServices.cheapest=nomic-embed-text:latest
1010
#
1111
#embabel.agent-platform.ranking.llm=llama3.1:8b
12+
embabel.tyrell.coding-llm.model=claude-opus-4-20250514

src/main/resources/prompts/.gitkeep

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Create a new Embabel agent in Java within this project.
2+
This project uses normal Maven conventions.
3+
4+
The package should be {{ pgk }}
5+
6+
The purpose of the agent is {{ purpose }}.
7+
8+
Choose an appropriate class name. Refer to API documentation and examples for how to build an agent.
9+
10+
Start by considering the data flow between the agent's @Action methods.
11+
These types should be captured in records.
12+
13+
Ue modern Java with vars and records.
14+
15+
Use the build tool to check that your build works and fix it if it doesn't.
16+
17+
You should also create the following artifacts:
18+
19+
1. Unit test for the new agent. Use WriteAndReviewAgentTest in this repository as a guide
20+
21+
{#2. Integration test for the new agent. Use#}
22+
23+
DO NOT MODIFY ANY FILES BESIDES THE NEW FILES YOU'VE CREATED
24+
25+
DO NOT MODIFY THE pom.xml
26+
27+
Tools provided will give you access to reference repositories.
28+
The tool names beginning with "this_project_" will give you access to this repository

0 commit comments

Comments
 (0)