Skip to content

Commit

Permalink
feat: Support custom constraints for an agent (#274)
Browse files Browse the repository at this point in the history
Enable custom constraints for an agent. Several users have requested to
be able to fine tune the behaviour of the agent.

Fixes #209
  • Loading branch information
timonv authored Feb 9, 2025
1 parent 973686d commit 64c86b6
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,10 @@ You can mix and match models from different providers for different tasks.

#### Other configuration

- **`agent_custom_constraints`**: Additional constraints / instructions for the agent.
These are passes to the agent in the system prompt and are rendered in a list. If you
intend to use more complicated instructions, consider adding a file to read in the
repository instead.
- **`cache_dir`, `log_dir`**: Directories for cache and logs. Defaults are within your system's cache directory.
- **`indexing_concurrency`**: Adjust concurrency for indexing, defaults based on CPU count.
- **`indexing_batch_size`**: Batch size setting for indexing. Defaults to a higher value for Ollama and a lower value for OpenAI.
Expand Down
56 changes: 50 additions & 6 deletions src/agent/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ pub async fn start(
}

pub fn build_system_prompt(repository: &Repository) -> Result<Prompt> {
let mut constraints = vec![
let mut constraints: Vec<String> = vec![
// General
"Research your solution before providing it",
"Tool calls are in parallel. You can run multiple tool calls at the same time, but they must not rely on each other",
Expand Down Expand Up @@ -325,28 +325,72 @@ pub fn build_system_prompt(repository: &Repository) -> Result<Prompt> {
"If you are stuck, consider using reset_file to undo your changes",
"Focus on completing the task fully as requested by the user",
"Do not repeat your answers, if they are exactly the same you should probably stop",
];
].into_iter().map(Into::into).collect();

if repository.config().agent_edit_mode.is_line() {
constraints = [constraints.as_slice(), &[
constraints.extend( [
"Prefer editing files with `replace_lines` and `add_lines` over `write_file`, if possible. This is faster and less error prone. You can only make ONE `replace_lines` or `add_lines` call at the time. After each you MUST call `read_file_with_line_numbers` again, as the linenumbers WILL have changed.",
"If you are only adding NEW lines, you MUST use `add_lines`",
"Before every call to `replace_lines` or `add_lines`, you MUST read the file content with the line numbers. You are not allowed to count lines yourself.",

]].concat();
].into_iter().map(Into::into));
}

if repository.config().endless_mode {
constraints.push("You cannot ask for feedback and have to try to complete the given task");
constraints
.push("You cannot ask for feedback and have to try to complete the given task".into());
} else {
constraints.push(
"Try to solve the problem yourself first, only if you cannot solve it, ask for help",
"Try to solve the problem yourself first, only if you cannot solve it, ask for help"
.into(),
);
}

if let Some(agent_custom_constraints) = repository.config().agent_custom_constraints.as_ref() {
constraints.extend(agent_custom_constraints.iter().cloned());
}

let prompt = SystemPrompt::builder()
.role(format!("You are an autonomous ai agent tasked with helping a user with a code project. You can solve coding problems yourself and should try to always work towards a full solution. The project is called {} and is written in {}", repository.config().project_name, repository.config().language))
.constraints(constraints).build()?.into();

Ok(prompt)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::test_repository;

#[tokio::test]
async fn test_build_system_prompt_endless_mode() {
let (mut repository, _guard) = test_repository();
repository.config_mut().endless_mode = true;
let prompt = build_system_prompt(&repository).unwrap();

assert!(prompt
.render()
.await
.unwrap()
.contains("You cannot ask for feedback and have to try to complete the given task"));
}

#[tokio::test]
async fn test_build_system_prompt_custom_constraints() {
let custom_constraints = vec![
"Custom constraint 1".to_string(),
"Custom constraint 2".to_string(),
];

let (mut repository, _guard) = test_repository();
repository.config_mut().agent_custom_constraints = Some(custom_constraints);

let prompt = build_system_prompt(&repository)
.unwrap()
.render()
.await
.unwrap();
assert!(prompt.contains("Custom constraint 1"));
assert!(prompt.contains("Custom constraint 2"));
}
}
8 changes: 8 additions & 0 deletions src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ pub struct Config {
/// How the agent will edit files, defaults to whole
#[serde(default)]
pub agent_edit_mode: AgentEditMode,

/// Additional constraints / instructions for the agent
///
/// These are passes to the agent in the system prompt and are rendered in a list. If you
/// intend to use more complicated instructions, consider adding a file to read in the
/// repository instead.
#[serde(default)]
pub agent_custom_constraints: Option<Vec<String>>,
}

fn default_otel_enabled() -> bool {
Expand Down

0 comments on commit 64c86b6

Please sign in to comment.